package ldh.im.monitor.controller;

import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
import ldh.im.ImConfig;
import ldh.im.dto.business.user.LoginReq;
import ldh.im.dto.business.user.LoginResp;
import ldh.im.dto.business.user.LoginTokenReq;
import ldh.im.exception.BusinessException;
import ldh.im.fxbase.cell.ObjectListCell;
import ldh.im.fxbase.fxanimations.FadeOutTransition;
import ldh.im.fxbase.util.*;
import ldh.im.monitor.dao.ImMonitorUserDao;
import ldh.im.monitor.pojo.ImMonitorUser;
import ldh.im.monitor.rpc.RpcMonitorLoginService;
import ldh.im.util.ConfigUtil;
import ldh.rpc.CodecType;
import ldh.rpc.TerminalType;
import ldh.rpc.cache.CacheService;
import ldh.rpc.client.RpcClientService;
import ldh.rpc.dto.CodecReq;
import ldh.rpc.exception.RpcException;
import org.kordamp.ikonli.fontawesome.FontAwesome;
import org.kordamp.ikonli.javafx.FontIcon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.Future;
import java.util.function.Consumer;

public class MonitorLoginController implements Initializable {

    private static final Logger LOGGER = LoggerFactory.getLogger(MonitorLoginController.class);

    @FXML private Pane root;
    @FXML private Label errorLoginTip;
    @FXML private ProgressIndicator loginProgress;
    @FXML private CheckBox rememberMe;
    @FXML private ComboBox<ImMonitorUser> userNameComboBox;
    @FXML private PasswordField passwordField;
    @FXML private Button loginBtn;

    private Stage loginStage;
    private Consumer<?> consumer = null;
    private CacheService cacheService;

    private boolean isNoPasswordLogin = false;  // 是否无密登录
    private boolean isCancel = false;
    private Future loginFuture = null;
    private RpcClientService codecTypeService = RpcUtil.getRpcBootstrap().getRpcConfig().getClientBean(RpcClientService.class);

    private RpcMonitorLoginService rpcMonitorLoginService = RpcUtil.getRpcBootstrap().getRpcConfig().getClientBean(RpcMonitorLoginService.class);

    private ImMonitorUserDao imMonitorUserDao = BeanFactory.getInstance().getBean(ImMonitorUserDao.class);

    public Scene createScene() {
        Scene scene = new Scene(root, 450, 330);
        scene.setFill(Color.TRANSPARENT);
        scene.getStylesheets().add(this.getClass().getResource("/css/var.css").toExternalForm());
        scene.getStylesheets().add(this.getClass().getResource("/css/monitor-login.css").toExternalForm());
        scene.getStylesheets().add(this.getClass().getResource("/css/bootstrapfx.css").toExternalForm());
        return scene;
    }

    public void setLoginStage(Stage stage) {
        this.loginStage = stage;
    }

    public void setCacheService(CacheService cacheService) {
        this.cacheService = cacheService;
    }

    public void setConsumer(Consumer<?> consumer) {
        this.consumer = consumer;
    }

    @FXML
    private void closeAct(ActionEvent actionEvent) {
        close(null, (Void)->{
            StageUtil.close();
            return null;
        });
    }

    @FXML
    public void loginAct(ActionEvent actionEvent) {
        if(!checkForm()) {
            return;
        }
        loginBtn.setDisable(true);
        loginProgress.setVisible(true);
        isCancel = false;
        errorLoginTip.setText("登录中......");
        String userId = userNameComboBox.getValue().getMobile();
        String password = passwordField.getText();
        Object userData = passwordField.getUserData();
        String token = userNameComboBox.getValue().getToken();
        loginFuture = ThreadPoolUtil.submit(()->{
            boolean isLoginSuccess = loginAction(userId, password, userData, token);
            LOGGER.info("login result:" + isLoginSuccess);
            if (!isLoginSuccess) return;
            if (isCancel) {
                resetLogin(null);
                return;
            }
            FxThreadUtil.runLater(()->errorLoginTip.setText("加载数据中......"));

            if (consumer != null) {
                consumer.accept(null);
            }

            FxThreadUtil.runLater(()->{
                close((Void)->{}, null);
            });
        }, (errorMsg)->{
            LOGGER.error("登录异常", errorMsg);
            String errorTip = isCancel ? "登录被取消" : "登录错误";
            resetLogin(errorTip);
        });
    }

    @FXML
    public void cancelAct(ActionEvent actionEvent) {
        loginProgress.setVisible(false);
        errorLoginTip.setText("");
        loginBtn.setDisable(false);
        if (loginFuture != null) {
            loginFuture.cancel(true);
        }
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        MovableHelper.movable(root);

        userNameComboBox.requestFocus();

        userNameComboBox.setConverter(new StringConverter<ImMonitorUser>() {

            @Override
            public String toString(ImMonitorUser imMonitorUser) {
                if(imMonitorUser == null) return "";
                String u = imMonitorUser.getMobile();
                return u;
            }

            @Override
            public ImMonitorUser fromString(String s) {
                if (s != null && !s.trim().equals("")) {
                    List<ImMonitorUser> imMonitorUserList = userNameComboBox.getItems();
                    Optional<ImMonitorUser> loginPoOptional = imMonitorUserList.stream().filter(l->l.getMobile().equals(s)).findFirst();
                    if (loginPoOptional.isPresent()) return loginPoOptional.get();
                    ImMonitorUser imMonitorUser = new ImMonitorUser();
                    imMonitorUser.setMobile(s);
                    return imMonitorUser;
                }
                return null;
            }
        });

        userNameComboBox.getSelectionModel().selectedItemProperty().addListener((ob, o, n)->{
            if (userNameComboBox.getSelectionModel().getSelectedItem() == null) {
                return;
            }
            String password = userNameComboBox.getSelectionModel().getSelectedItem().getMaskPassword();
            if (password == null) password = "";
            if (!password.equals("")) {
                userNameComboBox.setEditable(false);
                passwordField.setUserData(password);
            }
            if (o == n) return;
            passwordField.setText(password);
            if (password.equals("") && userNameComboBox.getValue().getMobile().equals("")) {
                if (userNameComboBox.isEditable()) return;
                passwordField.setUserData(null);
                isNoPasswordLogin = false;
                passwordField.requestFocus();
                userNameComboBox.setEditable(true);
                userNameComboBox.requestFocus();
            } else {
                isNoPasswordLogin = true;
                passwordField.requestFocus();
            }
        });

        userNameComboBox.setCellFactory(new ObjectListCell<ImMonitorUser>(imMonitorUser ->{
            Label label = new Label();
            label.setPadding(new Insets(0, 0, 0, 10));
            label.setText(imMonitorUser.getMobile());
            FontIcon fontIcon = FontIcon.of(FontAwesome.findByDescription("fa-user"));
            if (imMonitorUser.getMobile().equals("")) {
                fontIcon = FontIcon.of(FontAwesome.findByDescription("fa-user-plus"));
            }

            label.setGraphic(fontIcon);
            return label;
        }));

        ImMonitorUser nullImLogin = new ImMonitorUser();
        nullImLogin.setMaskPassword("");
        nullImLogin.setMobile("");
        userNameComboBox.getItems().addAll(nullImLogin);

        ThreadPoolUtil.submit(()->{
            try {
                List<ImMonitorUser> imMonitorUserList = imMonitorUserDao.getLogins();
                ImMonitorUser lastedLogin = getLastedLogin(imMonitorUserList);
                FxThreadUtil.runLater(()-> {
                    userNameComboBox.getItems().addAll(imMonitorUserList);
                    if (lastedLogin != null) {
                        userNameComboBox.getSelectionModel().select(lastedLogin);
                    }
                    loginBtn.requestFocus();
                });
            } catch (Exception e) {
                LOGGER.error("获取无秘登录用户失败", e);
            }
        });
    }

    private ImMonitorUser getLastedLogin(List<ImMonitorUser> imMonitorUserList) {
        if (imMonitorUserList.size() < 1) return null;
        return imMonitorUserList.stream().sorted((l1, l2)->l2.getUpdateTime().compareTo(l1.getUpdateTime())).findFirst().get();
    }

    private void resetLogin(String loginErrorMsg) {
        loginFuture = null;
        isCancel = false;
        String errorTip = loginErrorMsg != null ? loginErrorMsg : "";
        FxThreadUtil.runLater(()-> {
            loginBtn.setDisable(false);
            errorLoginTip.setText(errorTip);
            loginProgress.setVisible(false);
        });
    }

    private boolean loginAction(String mobile, String password, Object userData, String token) {
        ImMonitorUser imMonitorUser = new ImMonitorUser();
        imMonitorUser.setMobile(mobile);
        try {
            LoginResp loginResp = null;
            long time = System.currentTimeMillis();
            CodecType codecType = RpcUtil.getRpcBootstrap().getRpcConfig().getClientCodecType();
            cacheService.setCache(ImConfig.CODEC_TYPE, codecType, null);
            codecTypeService.codecType(new CodecReq(codecType));
            LOGGER.info("login time1: {}", System.currentTimeMillis() - time);
            boolean isLoginByToken = false;
            if (isNoPasswordLogin && userData != null && userData.toString().equals(password)) {
                LoginTokenReq loginReq = new LoginTokenReq(mobile, token, TerminalType.pc);
                loginReq.setTenantCode(ConfigUtil.getString("im.tenantCode"));
                loginReq.setMachineCode(MacUtils.generateMachineKey());
                isLoginByToken = true;
                loginResp = rpcMonitorLoginService.loginByToken(loginReq);
                LOGGER.info("login time2: {}", System.currentTimeMillis() - time);
            } else {
                LoginReq loginReq = new LoginReq(mobile, password, TerminalType.pc);
                loginReq.setTenantCode(ConfigUtil.getString("im.tenantCode"));
                loginReq.setMachineCode(MacUtils.generateMachineKey());
                loginResp = rpcMonitorLoginService.login(loginReq);
            }
            if (loginResp.isSuccess()) {
                cacheService.setCache(ImConfig.LOGIN_USER_ID, loginResp.getUserId(), null);
                cacheService.setCache(ImConfig.TENANT_ID, loginResp.getTenantCode(), null);
                cacheService.setCache(ImConfig.AUTH_TOKEN, loginResp.getAuthToken(), null);
                cacheService.setCache(ImConfig.AUTH_CODE, loginResp.getAuthCode(), null);
                if (!isLoginByToken) {
                    imMonitorUser.setToken(loginResp.getToken());
                }
                imMonitorUser.setMaskPassword(buildMaskPassword(password));
                imMonitorUser.setUpdateTime(new Date());
                saveOrUpdateMonitorUser(imMonitorUser);
                long loginTime = System.currentTimeMillis() - time;
                LOGGER.info("login time: {}", loginTime);
                return true;
            } else {
                String errorMsg = loginResp.getOtherInfo() != null ? loginResp.getOtherInfo() : "系统错误" ;
                FxThreadUtil.runLater(()->{
                    errorLoginTip.setText(errorMsg);
                    loginBtn.setDisable(false);
                    loginProgress.setVisible(false);
                });
            }
        } catch (Exception e) {
            LOGGER.error("登录错误", e);
            FxThreadUtil.runLater(()->{
                userNameComboBox.setEditable(true);
                if (e instanceof RpcException) {
                    RpcException rpcException = (RpcException) e;
                    String errorMessage = rpcException.getMessage();
                    int idx = errorMessage.lastIndexOf(":");
                    if (idx > 0) {
                        errorMessage = errorMessage.substring(idx+1);
                    }
                    errorLoginTip.setText(errorMessage);
                } else if (e instanceof BusinessException) {
                    if (e.getCause() != null) {
                        passwordField.setText("");
                        errorLoginTip.setText(e.getCause().getMessage());
                    } else {
                        errorLoginTip.setText(e.getMessage());
                    }
                } else if (e.getCause() instanceof BusinessException) {
                    errorLoginTip.setText(e.getCause().getMessage());
                } else {
                    errorLoginTip.setText("连接失败");
                }
                loginBtn.setDisable(false);
                loginProgress.setVisible(false);
            });
        }
        return false;
    }

    private void close(Consumer<?> consumer, Callback callback) {
        FadeOutTransition fadeOutTransition = new FadeOutTransition(root);
        fadeOutTransition.setOnFinished(e->{
            if (callback != null) callback.call(null);
            if(loginStage != null) loginStage.close();
            if (consumer != null) {
                consumer.accept(null);
            }
        });
        fadeOutTransition.playFromStart();
    }

    private String buildMaskPassword(String password) {
        String maskPassword = "";
        for (int i=0; i<password.length(); i++) {
            maskPassword += "1";
        }
        return maskPassword;
    }

    private void saveOrUpdateMonitorUser(ImMonitorUser imMonitorUser) throws SQLException {
        if (!rememberMe.isSelected()) {
            imMonitorUser.setMaskPassword("");
            imMonitorUser.setToken("");
            imMonitorUserDao.updateLogin(imMonitorUser);
            return;
        }
        ImMonitorUser existed = imMonitorUserDao.getLoginByUserId(imMonitorUser.getMobile());
        if (existed == null) {
            imMonitorUserDao.insertLogin(imMonitorUser);
        } else {
            imMonitorUser.setUpdateTime(new Date());
            imMonitorUserDao.updateLogin(imMonitorUser);
        }
    }

    private boolean checkForm() {
        if (userNameComboBox.getValue() == null || StringUtil.isEmpty(userNameComboBox.getValue().getMobile())) {
            errorLoginTip.setText("用户名不能为空");
            return false;
        }
        if (StringUtil.isEmpty(passwordField.getText())) {
            errorLoginTip.setText("密码不能为空");
            return false;
        }
        return true;
    }
}
